﻿//+-------------------------------------------------------------------------------------------------------------------------------------------------------
//
//Description: NetworkAdapterViewModel
//Author: Aravind Ramarathinam (aravr@microsoft.com)
//Copyright © Microsoft Corporation
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
//INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
// 
//--------------------------------------------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using VmroleAuthor.Models;

namespace VmroleAuthor.ViewModels
{
    public class NetworkAdapterViewModel : TreeViewItemViewModel, IDataErrorInfo, IProduceValidationResults, ICanBeRemovedFromTree
    {
        private readonly NetworkAdapter networkAdapter;
        private readonly VmRoleResourceDefinitionViewModel resourceDefinitionViewModel;

        private StringParameterizableProperty name;
        private StringParameterizableProperty networkRef;


        private StringParameterizableProperty ipv4AddressConfigurationName;     
        private EnumParameterizableProperty ipv4AddressAllocationMethod;
        private LoadBalancerConfigurationViewModel ipv4LoadBalancerConfigurationViewModel;


        private StringParameterizableProperty ipv6AddressConfigurationName;
        private EnumParameterizableProperty ipv6AddressAllocationMethod;
        private LoadBalancerConfigurationViewModel ipv6LoadBalancerConfigurationViewModel;

        private IPAddress ipV4Address;
        private IPAddress ipV6Address;


        public static class PropertyNames
        {
            public const string Summary = "Summary";
            public const string Name = "Name";
            public const string NetworkRef = "NetworkRef";
            public const string Ipv4AddressTypeEnabled = "Ipv4AddressTypeEnabled";
            public const string Ipv4AddressConfigurationName = "Ipv4AddressConfigurationName";       
            public const string Ipv4AddressAllocationMethod = "Ipv4AddressAllocationMethod";
            public const string LoadBalancedBehindIPv4VIP = "LoadBalancedBehindIPv4VIP";
            public const string IPv4LoadBalancerConfiguration = "IPv4LoadBalancerConfiguration";
            public const string Ipv6AddressTypeEnabled = "Ipv6AddressTypeEnabled";
            public const string Ipv6AddressConfigurationName = "Ipv6AddressConfigurationName";
            public const string Ipv6AddressAllocationMethod = "Ipv6AddressAllocationMethod";
            public const string LoadBalancedBehindIPv6VIP = "LoadBalancedBehindIPv6VIP";
            public const string IPv6LoadBalancerConfiguration = "IPv6LoadBalancerConfiguration";
        }

        public NetworkAdapterViewModel(
            NetworkAdapter networkAdapter,
            NetworkProfileViewModel networkProfileViewModel,
            VmRoleResourceDefinitionViewModel resourceDefinitionViewModel)
            : base(networkProfileViewModel, true)
        {
            if (networkAdapter == null)
            {
                networkAdapter = new NetworkAdapter();
            }
            this.networkAdapter = networkAdapter;
            this.resourceDefinitionViewModel = resourceDefinitionViewModel;
            if (this.networkAdapter.IPAddresses == null)
            {
                this.networkAdapter.IPAddresses = new List<IPAddress>();
            }
            foreach (IPAddress ipAddress in this.networkAdapter.IPAddresses)
            {              
               
                if ((string.Compare(ipAddress.Type, IpProtocolVersion.IPV6.ToString(), StringComparison.OrdinalIgnoreCase) == 0) && (ipV6Address == null))
                {
                    ipV6Address = ipAddress;
                }
                else
                {
                    if (
                        ((string.Compare(ipAddress.Type, IpProtocolVersion.IPV4.ToString(), StringComparison.OrdinalIgnoreCase) == 0) || (ParameterizationHelper.IsFullyParameterized(ipAddress.Type)))
                        && (ipV4Address == null)
                        )
                    {
                        ipV4Address = ipAddress;
                    }                   
                }
            }

            this.ValidationResults = new ObservableCollection<CloudServiceModelValidationResult>();  
       
            if (this.resourceDefinitionViewModel.IsNewResdefCreatedByTheTool)
            {
                this.NetworkRef.PropertyValue = ParametersViewModel.AutoGenerateMoniker;
            }
        }

        public NetworkAdapter NetworkAdapter
        {
            get { return this.networkAdapter; }
        }


        public string Summary
        {
            get { return "Network Adapter (" + this.networkAdapter.Name + ")"; }
        }

        public StringParameterizableProperty Name
        {
            get
            {
                if (this.name == null)
                {
                    Func<string> paramValueGetter = () => this.networkAdapter.Name;
                    Action<string> paramValueSetter = delegate(string value)
                        {
                            this.networkAdapter.Name = value;
                            this.OnPropertyChanged(PropertyNames.Summary);
                        };
                    Func<string> autoParamNameGetter = () => ParametersViewModel.VMRolePrefix + "NetworkAdapterName";
                    ViewDefParamInfo viewDefParamInfo = new ViewDefParamInfo
                    {
                        ViewDefParameterType = ViewDefParameterType.String,                     
                        DefaultEnglishLabelText = "Network Adapter Name"                       
                    };

                    this.name = new StringParameterizableProperty(
                        PropertyNames.Name,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        ParameterType.String,
                        "Network Adapter Name",
                        this.resourceDefinitionViewModel.ResourceDefinitionParametersViewModel,
                        true,
                        viewDefParamInfo);
                }
                return this.name;
            }
        }

        public StringParameterizableProperty NetworkRef
        {
            get
            {
                if (this.networkRef == null)
                {
                    Func<string> paramValueGetter = () => this.networkAdapter.NetworkRef;
                    Action<string> paramValueSetter = delegate(string value)
                    {
                        this.networkAdapter.NetworkRef = value;                        
                    };
                    Func<string> autoParamNameGetter = () => ParametersViewModel.VMRolePrefix + PropertyNames.NetworkRef;
                    ViewDefParamInfo viewDefParamInfo = new ViewDefParamInfo
                    {
                        IsRecommendedToBeParameterized = true,
                        ViewDefParameterType = ViewDefParameterType.Network,
                        DefaultEnglishLabelText = "Network reference"                    
                    };

                    this.networkRef = new StringParameterizableProperty(
                        PropertyNames.NetworkRef,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        ParameterType.String,
                        "Network reference",
                        this.resourceDefinitionViewModel.ResourceDefinitionParametersViewModel,
                        false,
                        viewDefParamInfo);
                }
                return this.networkRef;
            }
        }   

      
        public bool Ipv4AddressTypeEnabled
        {
            get
            {
                return this.ipV4Address != null;
            }
            set
            {
                if (value)
                {
                   if (this.ipV4Address == null)
                   {
                       this.ipV4Address = new IPAddress
                           {
                               AllocationMethod = IPAllocationMethod.Dynamic.ToString(),
                               Type = IpProtocolVersion.IPV4.ToString(),
                               ConfigurationName = "IPV4 Address Configuration"
                           };
                       this.networkAdapter.IPAddresses.Add(this.ipV4Address);
                       if (this.ipv4AddressConfigurationName != null)
                       {
                           this.ipv4AddressConfigurationName.IsRequired = true;                        
                       }
                       if (this.ipv4AddressAllocationMethod != null)
                       {
                           this.ipv4AddressAllocationMethod.IsRequired = true;                           
                       }                       
                   }
                  
                }
                else
                {
                   
                    if (this.ipv4AddressConfigurationName != null)
                    {
                        this.ipv4AddressConfigurationName.IsRequired = false;
                        this.ipv4AddressConfigurationName.PropertyValue = null;                        
                    }
                    if (this.ipv4AddressAllocationMethod != null)
                    {
                        this.ipv4AddressAllocationMethod.IsRequired = false;
                        this.ipv4AddressAllocationMethod.PropertyValue = null;                        
                    }
                    this.ipV4Address = null;
                    this.networkAdapter.IPAddresses.RemoveAll(ipAddress => string.Compare(ipAddress.Type, IpProtocolVersion.IPV4.ToString(), StringComparison.OrdinalIgnoreCase) == 0);
                   
                }
                this.OnPropertyChanged(PropertyNames.Ipv4AddressTypeEnabled);
                this.OnPropertyChanged(PropertyNames.Ipv4AddressConfigurationName);
                this.OnPropertyChanged(PropertyNames.Ipv4AddressAllocationMethod);
            }
        }

        public StringParameterizableProperty Ipv4AddressConfigurationName
        {
            get
            {
                if (this.ipv4AddressConfigurationName == null)
                {
                    Func<string> paramValueGetter = () => this.ipV4Address != null ? this.ipV4Address.ConfigurationName : null;
                    Action<string> paramValueSetter = delegate(string value) { if (this.ipV4Address != null) this.ipV4Address.ConfigurationName = value; };
                    Func<string> autoParamNameGetter = () => ParametersViewModel.VMRolePrefix + PropertyNames.Ipv4AddressConfigurationName;
                    ViewDefParamInfo viewDefParamInfo = new ViewDefParamInfo
                    {
                        ViewDefParameterType = ViewDefParameterType.String,                      
                        DefaultEnglishLabelText = "IPV4 Address Configuration"                        
                    };

                    this.ipv4AddressConfigurationName = new StringParameterizableProperty(
                        PropertyNames.Ipv4AddressConfigurationName,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        ParameterType.String,
                        "IPV4 Address Configuration",
                        this.resourceDefinitionViewModel.ResourceDefinitionParametersViewModel,
                        this.Ipv4AddressTypeEnabled,
                        viewDefParamInfo
                        );
                }
                return this.ipv4AddressConfigurationName;
            }
        }       

        /// <summary>
        /// IPAllocationMethod
        /// </summary>
        public EnumParameterizableProperty Ipv4AddressAllocationMethod
        {
            get
            {
                if (this.ipv4AddressAllocationMethod == null) 
                {
                    Func<string> paramValueGetter = () => this.ipV4Address != null ? this.ipV4Address.AllocationMethod : null;
                    Action<string> paramValueSetter = delegate(string value) { if (this.ipV4Address != null) this.ipV4Address.AllocationMethod = value; };
                    Func<string> autoParamNameGetter = () => ParametersViewModel.VMRolePrefix + PropertyNames.Ipv4AddressAllocationMethod;
                    ViewDefParamInfo viewDefParamInfo = new ViewDefParamInfo
                    {
                        ViewDefParameterType = ViewDefParameterType.Option,                      
                        DefaultEnglishLabelText = "IPV4 Address Allocation Method",                       
                        Options = new List<string>(Enum.GetNames(typeof(IPAllocationMethod)))
                    };

                    this.ipv4AddressAllocationMethod = new EnumParameterizableProperty(
                        PropertyNames.Ipv4AddressAllocationMethod,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        ParameterType.String,
                        "IPV4 Address Allocation Method",
                        this.resourceDefinitionViewModel.ResourceDefinitionParametersViewModel,
                        typeof(IPAllocationMethod),
                        this.Ipv4AddressTypeEnabled,
                        viewDefParamInfo);
                }
                return this.ipv4AddressAllocationMethod;
            }
        }



        public bool LoadBalancedBehindIPv4VIP
        {
            get
            {
                return this.ipV4Address != null && this.ipV4Address.LoadBalancerConfigurations != null &&
                       this.ipV4Address.LoadBalancerConfigurations.Count > 0;
            }
            set
            {
                if (value)
                {
                    this.Ipv4AddressTypeEnabled = true;
                    this.ipV4Address.AllocationMethod = IPAllocationMethod.Static.ToString();
                    if (this.ipV4Address.LoadBalancerConfigurations == null)
                    {
                        this.ipV4Address.LoadBalancerConfigurations = new List<LoadBalancerConfiguration>();
                    }
                    if (this.ipV4Address.LoadBalancerConfigurations.Count == 0)
                    {
                        this.ipV4Address.LoadBalancerConfigurations.Add(new LoadBalancerConfiguration());
                    }
                    this.IPv4LoadBalancerConfiguration.LoadBalancerConfiguration = this.ipV4Address.LoadBalancerConfigurations[0];
                }
                else
                {
                    this.IPv4LoadBalancerConfiguration.Cleanup();
                    this.IPv4LoadBalancerConfiguration.LoadBalancerConfiguration = null;
                    if (this.ipV4Address != null)
                    {
                        this.ipV4Address.LoadBalancerConfigurations = null;
                    }                   
                }              
                this.OnPropertyChanged(PropertyNames.LoadBalancedBehindIPv4VIP);
                this.OnPropertyChanged(PropertyNames.IPv4LoadBalancerConfiguration);
            }
        }


        public LoadBalancerConfigurationViewModel IPv4LoadBalancerConfiguration
        {
            get
            {
                if (this.ipv4LoadBalancerConfigurationViewModel == null)
                {
                    LoadBalancerConfiguration lbconfig = null;
                    if ((this.ipV4Address != null)&& this.ipV4Address.LoadBalancerConfigurations != null && this.ipV4Address.LoadBalancerConfigurations.Count > 0)
                    {
                        lbconfig = this.ipV4Address.LoadBalancerConfigurations[0];
                    }
                    this.ipv4LoadBalancerConfigurationViewModel = new LoadBalancerConfigurationViewModel(lbconfig, this.resourceDefinitionViewModel);                   
                }
                return this.ipv4LoadBalancerConfigurationViewModel;
            }
        }


        public bool Ipv6AddressTypeEnabled
        {
            get
            {
                return this.ipV6Address != null;
            }
            set
            {
                if (value)
                {
                    if (this.ipV6Address == null)
                    {
                        this.ipV6Address = new IPAddress
                        {
                            AllocationMethod = IPAllocationMethod.Dynamic.ToString(),
                            Type = IpProtocolVersion.IPV6.ToString(),
                            ConfigurationName = "IPV6 Address Configuration"
                        };
                        this.networkAdapter.IPAddresses.Add(this.ipV6Address);
                        if (this.ipv6AddressConfigurationName != null)
                        {
                            this.ipv6AddressConfigurationName.IsRequired = true;                                      
                        }
                        if (this.ipv6AddressAllocationMethod != null)
                        {
                            this.ipv6AddressAllocationMethod.IsRequired = true;                                           
                        }          
                    }
                }
                else
                {                    
                    if (this.ipv6AddressConfigurationName != null)
                    {
                        this.ipv6AddressConfigurationName.IsRequired = false;
                        this.ipv6AddressConfigurationName.PropertyValue = null;                        
                    }
                    if (this.ipv6AddressAllocationMethod != null)
                    {
                        this.ipv6AddressAllocationMethod.IsRequired = false;
                        this.ipv6AddressAllocationMethod.PropertyValue = null;                       
                    }
                    this.ipv6AddressConfigurationName = null;
                    this.ipv6AddressAllocationMethod = null;
                    this.ipV6Address = null;
                    this.networkAdapter.IPAddresses.RemoveAll(ipAddress => string.Compare(ipAddress.Type, IpProtocolVersion.IPV6.ToString(), StringComparison.OrdinalIgnoreCase) == 0);                 
                }
                this.OnPropertyChanged(PropertyNames.Ipv6AddressTypeEnabled);
                this.OnPropertyChanged(PropertyNames.Ipv6AddressConfigurationName);
                this.OnPropertyChanged(PropertyNames.Ipv6AddressAllocationMethod);
            }
        }

        public StringParameterizableProperty Ipv6AddressConfigurationName
        {
            get
            {
                if (this.ipv6AddressConfigurationName == null)
                {
                    Func<string> paramValueGetter = () => this.ipV6Address!= null ? this.ipV6Address.ConfigurationName : null;
                    Action<string> paramValueSetter = delegate(string value) { if (this.ipV6Address != null) this.ipV6Address.ConfigurationName = value; };
                    Func<string> autoParamNameGetter = () => ParametersViewModel.VMRolePrefix + PropertyNames.Ipv6AddressConfigurationName;
                    ViewDefParamInfo viewDefParamInfo = new ViewDefParamInfo
                    {
                        ViewDefParameterType = ViewDefParameterType.String,
                        DefaultEnglishLabelText = "IPV6 Address Configuration",                       
                    };

                    this.ipv6AddressConfigurationName = new StringParameterizableProperty(
                        PropertyNames.Ipv6AddressConfigurationName,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        ParameterType.String,
                        "IPV6 Address Configuration",
                        this.resourceDefinitionViewModel.ResourceDefinitionParametersViewModel,
                        this.Ipv6AddressTypeEnabled,
                        viewDefParamInfo);
                }
                return this.ipv6AddressConfigurationName;
            }
        }

        /// <summary>
        /// IPAllocationMethod
        /// </summary>
        public EnumParameterizableProperty Ipv6AddressAllocationMethod
        {
            get
            {
                if (this.ipv6AddressAllocationMethod == null)
                {
                    Func<string> paramValueGetter = () => this.ipV6Address != null ? this.ipV6Address.AllocationMethod : null;
                    Action<string> paramValueSetter = delegate(string value) { if (this.ipV6Address != null) this.ipV6Address.AllocationMethod = value; };
                    Func<string> autoParamNameGetter = () => ParametersViewModel.VMRolePrefix + PropertyNames.Ipv6AddressAllocationMethod;
                    ViewDefParamInfo viewDefParamInfo = new ViewDefParamInfo
                    {
                        ViewDefParameterType = ViewDefParameterType.Option,
                        DefaultEnglishLabelText = "IPV6 Address Allocation Method",                       
                        Options = new List<string>(Enum.GetNames(typeof(IPAllocationMethod)))
                    };

                    this.ipv6AddressAllocationMethod = new EnumParameterizableProperty(
                        PropertyNames.Ipv6AddressAllocationMethod,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        ParameterType.String,
                        "IPV6 Address Allocation Method",
                        this.resourceDefinitionViewModel.ResourceDefinitionParametersViewModel,
                        typeof(IPAllocationMethod),
                        this.Ipv6AddressTypeEnabled,
                        viewDefParamInfo);
                }
                return this.ipv6AddressAllocationMethod;
            }
        }

        public bool LoadBalancedBehindIPv6VIP
        {
            get
            {
                return this.ipV6Address != null && this.ipV6Address.LoadBalancerConfigurations != null &&
                       this.ipV6Address.LoadBalancerConfigurations.Count > 0;
            }
            set
            {
                if (value)
                {
                    this.Ipv6AddressTypeEnabled = true;
                    this.ipV6Address.AllocationMethod = IPAllocationMethod.Static.ToString();
                    if (this.ipV6Address.LoadBalancerConfigurations == null)
                    {
                        this.ipV6Address.LoadBalancerConfigurations = new List<LoadBalancerConfiguration>();
                    }
                    if (this.ipV6Address.LoadBalancerConfigurations.Count == 0)
                    {
                        this.ipV6Address.LoadBalancerConfigurations.Add(new LoadBalancerConfiguration());
                    }
                    this.IPv6LoadBalancerConfiguration.LoadBalancerConfiguration = this.ipV6Address.LoadBalancerConfigurations[0];
                }
                else
                {
                    this.IPv6LoadBalancerConfiguration.Cleanup();
                    this.IPv6LoadBalancerConfiguration.LoadBalancerConfiguration = null;
                    if (this.ipV6Address != null)
                    {
                        this.ipV6Address.LoadBalancerConfigurations = null;
                    }                   
                }
                this.OnPropertyChanged(PropertyNames.LoadBalancedBehindIPv6VIP);             
                this.OnPropertyChanged(PropertyNames.IPv6LoadBalancerConfiguration);
            }
        }


        public LoadBalancerConfigurationViewModel IPv6LoadBalancerConfiguration
        {
            get
            {
                if (this.ipv6LoadBalancerConfigurationViewModel == null)
                {
                    LoadBalancerConfiguration lbconfig = null;
                    if ((this.ipV6Address != null) && this.ipV6Address.LoadBalancerConfigurations != null && this.ipV6Address.LoadBalancerConfigurations.Count > 0)
                    {
                        lbconfig = this.ipV6Address.LoadBalancerConfigurations[0];
                    }
                    this.ipv6LoadBalancerConfigurationViewModel = new LoadBalancerConfigurationViewModel(lbconfig, this.resourceDefinitionViewModel);
                }
                return this.ipv6LoadBalancerConfigurationViewModel;
            }
        }

        public void Cleanup()
        {
            this.IsExpanded = true;
            ParametersAndPayloadCleanupHelper.CleanupParametersAndPayload(this, typeof(PropertyNames));
        }

        public string this[string propertyName]
        {
            get
            {
                string error = null;
                switch (propertyName)
                {
                    case PropertyNames.LoadBalancedBehindIPv4VIP:
                        if (this.LoadBalancedBehindIPv4VIP)
                        {
                            if (!string.Equals(this.ipV4Address.AllocationMethod, IPAllocationMethod.Static.ToString(), StringComparison.OrdinalIgnoreCase))
                            {
                                error = "IPV4 Address Allocation Method has to be static when behind a load balancer";
                            }
                            else
                            {
                                if (this.LoadBalancedBehindIPv6VIP)
                                {
                                    error = "In Windows Azure Pack 2012 R2 release, the network adapter could be load balanced behind either IPV4 VIP or IPV6 VIP, but not both";
                                }
                                else if (this.ipV4Address != null && this.ipV4Address.LoadBalancerConfigurations.Count > 1)
                                {
                                    error = "In Windows Azure Pack 2012 R2 release, the network adapter could be load balanced behind only 1 VIP";
                                }
                            }                            
                        }
                                               
                        break;
                    case PropertyNames.LoadBalancedBehindIPv6VIP:
                        if (this.LoadBalancedBehindIPv6VIP)
                        {
                            if (!string.Equals(this.ipV6Address.AllocationMethod, IPAllocationMethod.Static.ToString(), StringComparison.OrdinalIgnoreCase))
                            {
                                error = "IPV6 Address Allocation Method has to be static when behind a load balancer";
                            }
                            else
                            {
                                if (this.LoadBalancedBehindIPv4VIP)
                                {
                                    error = "In Windows Azure Pack 2012 R2 release, the network adapter could be load balanced behind either IPV4 VIP or IPV6 VIP, but not both";
                                }
                                else if (this.ipV6Address != null && this.ipV6Address.LoadBalancerConfigurations.Count > 1)
                                {
                                    error = "In Windows Azure Pack 2012 R2 release, the network adapter could be load balanced behind only 1 VIP";
                                }
                            }                            
                        }                      
                        break;
                }
                return error;
            }
        }

       
        public string Error { get; set; }

        public ObservableCollection<CloudServiceModelValidationResult> ValidationResults { get; private set; }

        public void UpdateValidationResults()
        {
            this.IsExpanded = true;
            ModelValidationResultsHelper.UpdateValidationResults(this, typeof(PropertyNames));
            //Add any other cross-field validations error            
        }     
    }
}
